package com.k2data.k2app.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.k2data.k2app.common.MyConstant;
import com.k2data.k2app.common.OrikaMapperFactory;
import com.k2data.k2app.common.ResponseConstant;
import com.k2data.k2app.constant.Constant;
import com.k2data.k2app.domain.CustomerDTO;
import com.k2data.k2app.domain.DeviceCond;
import com.k2data.k2app.domain.DeviceDTO;
import com.k2data.k2app.domain.po.Customer;
import com.k2data.k2app.domain.po.Device;
import com.k2data.k2app.domain.po.EventFault;
import com.k2data.k2app.domain.po.RDeviceCustomer;
import com.k2data.k2app.domain.po.v1.Contacts;
import com.k2data.k2app.domain.po.v1.Event;
import com.k2data.k2app.domain.po.v1.RCustomerContacts;
import com.k2data.k2app.domain.po.v1.RFile;
import com.k2data.k2app.exception.K2ResponseException;
import com.k2data.k2app.mapper.*;
import com.k2data.k2app.rest.InstanceClient;
import com.k2data.k2app.rest.StaticClient;
import com.k2data.k2app.rest.SysClient;
import com.k2data.k2app.rest.domain.*;
import com.k2data.k2app.utils.ObjectUtils;
import com.k2data.k2app.utils.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.net.UnknownHostException;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collector;
import java.util.stream.Collectors;

/**
 * @author lidong9144@163.com 17-3-24.
 */
@Service
public class CustomerService {

    private final ConcurrentHashMap<String, DictResult> statusTypeCache;
    private final ConcurrentHashMap<String, DictResult> industryCache;
    private final ConcurrentHashMap<String, DictResult> dataSourceCache;

    private Cache<String, String> iconUrlCache = CacheBuilder.newBuilder()
            .maximumSize(50)
            .expireAfterAccess(5, TimeUnit.MINUTES)
            .softValues()
            .build();

    private final SysClient sysClient;
    private final StaticClient staticClient;
    private final InstanceClient instanceClient;

    private final CustomerMapper customerMapper;
    private final RDeviceCustomerMapper rDeviceCustomerMapper;

    @Autowired
    private RFileMapper rFileMapper;
    @Autowired
    private ContactsMapper contactsMapper;
    @Autowired
    private RCustomerContactsMapper rCustomerContactsMapper;
    @Autowired
    public CustomerService(CustomerMapper customerMapper, RDeviceCustomerMapper rDeviceCustomerMapper,
                           SysClient sysClient, StaticClient staticClient,
                           InstanceClient instanceClient) {
        this.customerMapper = customerMapper;
        this.rDeviceCustomerMapper = rDeviceCustomerMapper;
        this.sysClient = sysClient;
        this.staticClient = staticClient;
        this.instanceClient = instanceClient;

        this.statusTypeCache = new ConcurrentHashMap<>();
        this.industryCache = new ConcurrentHashMap<>();
        this.dataSourceCache = new ConcurrentHashMap<>();
    }

    @Transactional
    public Long insert(Customer po) throws K2ResponseException {
        po.setCustomerNo(getCustomerNo());
        RegionRest regionRest = sysClient.getRegion(null, po.getProvince(), po.getCity(), po.getDistrict());

        if (regionRest.getResult().getCode() == null) {
            throw new K2ResponseException(ResponseConstant.ID_NOT_FOUND,
                    String.format("Region code not found by province: %s, city: %s, district: %s.", po.getProvince(), po.getCity(), po.getDistrict()));
        }
        po.setRegionCode(regionRest.getResult().getCode());

        customerMapper.insertSelective(po);

        // 文件
        List<RFile> rFiles = RFile.updateFiles(po.getId(),po.getFileList(),"customer");
        if(rFiles!=null && rFiles.size()>0){
            rFileMapper.insertBatch(rFiles);
        }

        // 联系人关联

        if(po.getContacts() != null && po.getContacts().size()>0){
            for(Contacts contactse: po.getContacts()){
                contactse.setCustomerId(po.getId());
                contactsMapper.insert(contactse);
                //关联关系
                RCustomerContacts rCustomerContacts = new RCustomerContacts();
                rCustomerContacts.setContactsId(contactse.getId());
                rCustomerContacts.setCustomerId(po.getId());
                rCustomerContactsMapper.insert(rCustomerContacts);
            }
        }
        return po.getId();
    }

    @Transactional
    public Long update(Customer po) throws K2ResponseException {
        if (po.getProvince() != null) {
            RegionRest regionRest = sysClient.getRegion(null, po.getProvince(), po.getCity(), po.getDistrict());
            if (regionRest.getResult().getCode() == null) {
                throw new K2ResponseException(ResponseConstant.ID_NOT_FOUND,
                        String.format("Region code not found by province: %s, city: %s, district: %s.", po.getProvince(), po.getCity(), po.getDistrict()));
            }
            po.setRegionCode(regionRest.getResult().getCode());
        }

        int num = customerMapper.updateByPrimaryKeySelective(po);

        if (num == 0) {
            throw new K2ResponseException(ResponseConstant.ID_NOT_FOUND,
                    String.format("%s(id: %s)", ResponseConstant.MSG_CUSTOMER_ID_NOT_FOUND, po.getId()));
        }

        rFileMapper.deleteByCustomerId(po.getId(),"customer");
        List<RFile> rFiles = RFile.updateFiles(po.getId(),po.getFileList(),"customer");
        if(rFiles != null && rFiles.size()>0) {
           rFileMapper.insertBatch(rFiles);
        }

        rCustomerContactsMapper.deleteByCustomerId(po.getId());
        //联系人
        if(po.getContacts() != null && po.getContacts().size()>0){
            for(Contacts contactse: po.getContacts()){
                contactse.setCustomerId(po.getId());
                if(contactse.getId()==null){
                    contactsMapper.insert(contactse);
                }
                //关联关系
                RCustomerContacts rCustomerContacts = new RCustomerContacts();
                rCustomerContacts.setContactsId(contactse.getId());
                rCustomerContacts.setCustomerId(po.getId());
                rCustomerContactsMapper.insert(rCustomerContacts);
            }
        }
        return po.getId();
    }


    @Transactional
    public Long delete(Long id) {
        // 删除客户
        Customer po = new Customer();
        po.setId(id);
        customerMapper.delete(po);

        // 删除客户和设备的关联关系
        RDeviceCustomer r1 = new RDeviceCustomer();
        r1.setCustomerId(id);
        rDeviceCustomerMapper.delete(r1);

        // 删除和联系人关联关系
        RCustomerContacts rCustomerContacts = new RCustomerContacts();
        rCustomerContacts.setCustomerId(id);
        rCustomerContactsMapper.delete(rCustomerContacts);

        // 删除和附件的关联
        RFile rFile = new RFile();
        rFile.setCustomerId(id);
        rFileMapper.delete(rFile);

        return id;
    }

    private void refreshDictCache() {
        DictRest statusTypes = sysClient.get(MyConstant.DICT_DEVICE_STATUS_TYPE);
        for (DictResult dictResult : statusTypes.getResult()) {
            statusTypeCache.put(dictResult.getLabel(), dictResult);
        }

        DictRest industries = sysClient.get(MyConstant.DICT_INDUSTRY_TYPE);
        for (DictResult dictResult : industries.getResult()) {
            industryCache.put(dictResult.getLabel(), dictResult);
        }

        DictRest dataSources = sysClient.get(MyConstant.DICT_DATA_SOURCE_TYPE);
        for (DictResult dictResult : dataSources.getResult()) {
            dataSourceCache.put(dictResult.getLabel(), dictResult);
        }
    }

    public List<DeviceDTO> getDeviceCustomers(DeviceCond cond) throws UnknownHostException, K2ResponseException {
        List<DeviceDTO> deviceCustomerDTOS = Lists.newArrayList();

        // 判断用户是否是系统管理员
        if(cond.getUserId()!=null){
            RectList rolesByUser = sysClient.getRolesByUser(cond.getUserId());
            List list = rolesByUser.getResult();
            if(list!=null && list.size()>0){
                for(int i=0;i<list.size();i++){
                    Map map = (Map)list.get(i);
                    if(map.get("enname")!=null && !map.get("enname").toString().equals("Administrator")){
                    }else{
                        cond.setUserId(null);
                    }
                }
            }
        }


        List<Customer> list;
        if (cond.getStartDate() != null) {
            list = customerMapper.selectDeviceCustomersByDate(cond);
        } else {
            list = customerMapper.selectDeviceCustomers(cond);
        }

        if (list.isEmpty()) {
            return deviceCustomerDTOS;
        }

        refreshDictCache();

        for (Customer customer : list) {
            if(customer.getCustomerNo().startsWith("tcs")){
                customer.setCustomerNo(customer.getCustomerNo().split("_")[1]);
            }
            DeviceDTO dto = OrikaMapperFactory.getMapper().map(customer, DeviceDTO.class);

            RegionRest regionRest = sysClient.getRegion(customer.getRegionCode().toString(), null, null, null);
            dto.setRegion(regionRest.getResult());
            if (!StringUtils.isBlank(cond.getLocation()) && !cond.getLocation().equals(regionRest.getResult().getLocation())) {
                continue;
            }

            if (!StringUtils.isBlank(customer.getImageUrl())) {
                dto.setImageUrl(instanceClient.getGatewayUrl() + Paths.get("/static/", customer.getImageUrl()).toString());
            } else {
                dto.setImageUrl("");
            }

            // 如果没有关联设备，就 continue
            if (ObjectUtils.isEmpty(customer.getDevices())) {
                dto.setCount(0);
                dto.setOnlineCount(0);
                dto.setAlarmCount(0);
                dto.setIconUrl(getMapIconUrl(dto.getIndustry(), Lists.newArrayList(), ""));
                // 客户的文件信息
                if(customer.getFileList() != null && customer.getFileList().size()>0){
                    List<RFile> byCustomerId = customer.getFileList();
                    for(RFile rFile:byCustomerId){
                        rFile.setFileUrl(instanceClient.getGatewayUrl() + Paths.get("/static/", rFile.getFileUrl()).toString());
                    }
                    dto.setFileList(byCustomerId);
                }
                deviceCustomerDTOS.add(dto);
                continue;
            }

            // 以下字段取设备状态优先级最高的设备的值
            Map<String, Long> statuses = customer.getDevices()
                    .parallelStream()
                    .collect(Collectors.groupingBy(Device::getStatus, Collectors.counting()));
            dto.setStatuses(statuses);

            Set<String> dataSourceSet = Sets.newHashSet();  // 存放所有 数据来源 字段数据

            Integer tempPriority = -1;
            Device tempPo = new Device();
            String status = MyConstant.DEVICE_STATUS_OFFLINE;
            int score = 0;
            for (Device device : customer.getDevices()) {
                //--- 以哪个设备的状态为准
                int score1 = statusTypeCache.get(StringUtils.isEmpty(device.getStatus())?MyConstant.DEVICE_STATUS_OFFLINE:device.getStatus()).getSort();
                if(score==0){
                    score=score1;
                    status=device.getStatus();
                } else if(score>0 && score1>0){
                    score=score<score1?score:score1;
                    status=score<score1?status:device.getStatus();
                }
                //---

                DictResult dictPO = statusTypeCache.get(device.getStatus());
                if (dictPO == null) {
                    continue;
                }
                JSONObject ext = JSON.parseObject(dictPO.getExt());
                if (ext != null) {
                    Object priorityObj = ext.get(MyConstant.DEVICE_STATUS_PRIORITY);
                    Integer priority = priorityObj == null ? 0 : Integer.valueOf(priorityObj.toString());
                    if (priority > tempPriority) {
                        tempPriority = priority;
                        tempPo = device;
                    }

                    String customerValue = device.getCustomValue(Constant.DICT_TYPE_DATA_SOURCE);

                    if (!StringUtils.isBlank(customerValue)) {
                        dataSourceSet.add(customerValue);
                    }
                }
            }

            if (!dataSourceSet.isEmpty()) {
                List<String> dataSourceList = Lists.newArrayList(dataSourceSet);
                dataSourceList.sort(String::compareTo);
                dto.setDataSources(dataSourceList);
            }

//            dto.setPriorStatus(tempPo.getStatus() == null ? "" : tempPo.getStatus());
            dto.setPriorStatus(status);
//            Long statusCount = statuses.get(tempPo.getStatus());
            Long statusCount = statuses.get(status);
            dto.setPriorStatusCount(statusCount == null ? 0 : statusCount.intValue());

            dto.setIconUrl(getMapIconUrl(dto.getIndustry(), Lists.newArrayList(dataSourceSet), dto.getPriorStatus()));

            // 数量统计
            Integer count = customer.getDevices().size();
            dto.setCount(count);

            Long offlineCount = statuses.get(MyConstant.DEVICE_STATUS_OFFLINE);
            offlineCount = offlineCount == null ? 0 : offlineCount;
            dto.setOnlineCount(count - offlineCount.intValue());

            Long alarmCount = statuses.get(MyConstant.DEVICE_STATUS_ALARM);
            alarmCount = alarmCount == null ? 0 : alarmCount;
            Long faultCount = statuses.get(MyConstant.DEVICE_STATUS_FAULT);
            faultCount = faultCount == null ? 0 : faultCount;
            dto.setAlarmCount(alarmCount.intValue() + faultCount.intValue());
            // 客户的文件信息
            if(customer.getFileList() != null && customer.getFileList().size()>0){
                List<RFile> byCustomerId = customer.getFileList();
                for(RFile rFile:byCustomerId){
                    rFile.setFileUrl(instanceClient.getGatewayUrl() + Paths.get("/static/", rFile.getFileUrl()).toString());
                }
                dto.setFileList(byCustomerId);
            }

            // 客户的联系人信息
            if(customer.getContacts() != null && customer.getContacts().size()>0){
                dto.setContacts(customer.getContacts());
            }

            // 客户的权限

            deviceCustomerDTOS.add(dto);
        }
        //2018-01-24 处理经纬度：改为采用customer表中的经纬度，为了使前端不修改，改为将customer表中查询的经纬度赋值给region
        if(!deviceCustomerDTOS.isEmpty()){
            for (DeviceDTO dt:deviceCustomerDTOS) {
                if(dt.getLongitude()!=null){
                    dt.getRegion().setLongitude(dt.getLongitude());
                }
                if(dt.getLatitude()!=null){
                    dt.getRegion().setLatitude(dt.getLatitude());
                }
            }
        }
        return deviceCustomerDTOS;
    }

    public List<DeviceDTO> selectCustomerFault(DeviceCond cond) {
        List<DeviceDTO> list = Lists.newArrayList();

        List<Customer> sourceList = customerMapper.selectCustomerFault(cond);
        for (Customer customer : sourceList) {
            if (customer.getFaultsv2().isEmpty()) {
                continue;
            }

            DeviceDTO dto = OrikaMapperFactory.getMapper().map(customer, DeviceDTO.class);

            RegionRest regionRest = sysClient.getRegion(customer.getRegionCode().toString(), null, null, null);
            dto.setRegion(regionRest.getResult());

            Map<String, Long> typeCount = customer.getFaultsv2().stream()
                    .collect(Collectors.groupingBy(Event::getEventType, Collectors.counting()));

            dto.setFaults(typeCount);
            list.add(dto);
        }

        return list;
    }

    public String getMapIconUrl(String industry, List<String> dataSourceList, String status) {
        dataSourceList.sort(String::compareTo);
        String key = industry + dataSourceList.toString() + status;

        String value = iconUrlCache.getIfPresent(key);
        if (value != null) {
            return value;
        }

        String newColor = "";
        DictResult colorDict = statusTypeCache.get(status);
        if (colorDict != null && colorDict.getExt() != null) {
            newColor = JSON.parseObject(colorDict.getExt()).get("color").toString();
        }

        CombineParams combineParams = new CombineParams();
        combineParams.setOriginColor("#e2e2e2");
        combineParams.setNewColor(newColor);
        combineParams.setDefaultImage("/public/mapIcon.png");

        List<OverlayImage> overlayImages = Lists.newArrayList();

        int xShift = 10;
        int yShift = 2;

        if (!StringUtils.isBlank(industry)) {
            DictResult dict = industryCache.get(industry);
            if (dict != null && dict.getExt() != null && JSON.parseObject(dict.getExt()).get("image").toString() != null) {
                OverlayImage industryOverlay = new OverlayImage();
                industryOverlay.setImageName(JSON.parseObject(dict.getExt()).get("image").toString());
                industryOverlay.setX(xShift);
                industryOverlay.setY(yShift);
                overlayImages.add(industryOverlay);
            } else {
                OverlayImage industryOverlay = new OverlayImage();
                industryOverlay.setImageName("/public/industryDefault.png");
                industryOverlay.setX(xShift);
                industryOverlay.setY(yShift);
                overlayImages.add(industryOverlay);
            }
        } else {
            OverlayImage industryOverlay = new OverlayImage();
            industryOverlay.setImageName("/public/industryDefault.png");
            industryOverlay.setX(xShift);
            industryOverlay.setY(yShift);
            overlayImages.add(industryOverlay);
        }

        xShift += 12;

        int tempSize = overlayImages.size();

        for (String s : dataSourceList) {
            if (overlayImages.size() > tempSize) {
                OverlayImage dataSourceOverlay = new OverlayImage();
                dataSourceOverlay.setImageName("/public/slash.png");
                dataSourceOverlay.setX(xShift += 8);
                dataSourceOverlay.setY(6);
                overlayImages.add(dataSourceOverlay);
            }

            DictResult dict = dataSourceCache.get(s);
            if (dict != null && dict.getExt() != null && JSON.parseObject(dict.getExt()).get("image").toString() != null) {
                OverlayImage dataSourceOverlay = new OverlayImage();
                dataSourceOverlay.setImageName(JSON.parseObject(dict.getExt()).get("image").toString());
                dataSourceOverlay.setX(xShift += 4);
                dataSourceOverlay.setY(6);
                overlayImages.add(dataSourceOverlay);
            }
        }

        if (overlayImages.size() == 1) {
            combineParams.setImageName("/public/mapIcon.png");
            overlayImages.get(0).setX(21);
        }

        if (overlayImages.size() == 2) {
            combineParams.setImageName("/public/mapIconNarrow.png");
            overlayImages.get(0).setX(15);
            overlayImages.get(1).setX(32);
        }

        if (overlayImages.size() > 2) {
            combineParams.setImageName("/public/mapIconWide.png");
        }

        if (overlayImages.isEmpty()) {
            combineParams.setImageName("/public/mapIcon.png");

            OverlayImage defaultOverlay = new OverlayImage();
            defaultOverlay.setImageName("/public/mapIconDefault.png");
            defaultOverlay.setX(20);
            defaultOverlay.setY(2);
            overlayImages.add(defaultOverlay);
        }

        combineParams.setOverlayImages(overlayImages);

        String url = staticClient.combine(combineParams).getResult().getUrl();

        iconUrlCache.put(key, url);

        return url;
    }


    public List<RFile> getFileUrls(Long id) {
        List<RFile> byCustomerId = rFileMapper.getByCustomerId(id,"customer");
        for(RFile rFile:byCustomerId){
            rFile.setFileUrl(instanceClient.getGatewayUrl() + Paths.get("/static/", rFile.getFileUrl()).toString());
        }
        return byCustomerId;
    }

    public List<Contacts> getContactsByCustomer(Long id) {
        List<Contacts> byCustomer = contactsMapper.getByCustomer(id);
        return byCustomer;
    }

    public List<Contacts> getContactsByCustomerAll(Long id) {
        List<Contacts> byCustomer = contactsMapper.getByCustomerAll(id);
        List<Contacts> byCustomerSelf = contactsMapper.getByCustomer(id);
        for(Contacts c:byCustomerSelf){
            if(!byCustomer.contains(c)){
                byCustomer.add(c);
            }
        }
        return byCustomer;
    }

    public Map<String, List<CustomerDTO>> getAllDevices(String groupby) {
        List<Customer> source = customerMapper.selectCustomerDevices();

        List<CustomerDTO> list = Lists.newArrayList();
        for (Customer customer : source) {
            RegionRest regionRest = sysClient.getRegion(customer.getRegionCode().toString(), null, null, null);
            if (regionRest.getResult() == null || StringUtils.isBlank(customer.getIndustry())) {
                continue;
            }

            CustomerDTO customerDTO = OrikaMapperFactory.getMapper().map(customer, CustomerDTO.class);

            customerDTO.setRegion(regionRest.getResult());

            List<DeviceDTO> devices = Lists.newArrayList();
            for (Device device : customer.getDevices()) {
                DeviceDTO deviceDTO = OrikaMapperFactory.getMapper().map(device, DeviceDTO.class);
                devices.add(deviceDTO);
            }
            customerDTO.setDevices(devices);

            list.add(customerDTO);
        }

        Map<String, List<CustomerDTO>> groupResult = Maps.newHashMap();

        if ("location".equalsIgnoreCase(groupby)) {
            groupResult = list.stream()
                    .collect(Collectors.groupingBy(o -> o.getRegion().getLocation()));
        }

        if ("industry".equalsIgnoreCase(groupby)) {
            groupResult = list.stream()
                    .collect(Collectors.groupingBy(CustomerDTO::getIndustry));
        }

        return groupResult;
    }

    public  String getCustomerNo(){
        String customerNoMax = customerMapper.selectCustomerNoMax();
        if(customerNoMax != null) {
            customerNoMax = customerNoMax.split("_")[0]+"_"+autoGenericCode(customerNoMax.split("_")[1]);
        }else{
            customerNoMax = "c_000001";
        }
        return customerNoMax;
    }

    /**
     * 不够位数的在前面补0，保留num的长度位数字
     * @param code
     * @return
     */
    private static  String autoGenericCode(String code) {
        String result = "";
       // 保留num的位数
       // 0 代表前面补充0
       // num 代表长度为4
       // d 代表参数为正数型
        result = String.format("%0" + 6 + "d", Long.parseLong(code) + 1);
        return result;
    }


    /**
     * 行业分组
     * @param cond
     * @return
     */
    public Map<String,Integer>  getIndustryCount(DeviceCond cond,String showType) {
        List<Customer> result = Lists.newArrayList();
        // 判断用户是否是系统管理员
        if (cond.getUserId() != null) {
            RectList rolesByUser = sysClient.getRolesByUser(cond.getUserId());
            List list = rolesByUser.getResult();
            if (list != null && list.size() > 0) {
                for (int i = 0; i < list.size(); i++) {
                    Map map = (Map) list.get(i);
                    if (map.get("enname") != null && !map.get("enname").toString().equals("Administrator")) {
                    } else {
                        cond.setUserId(null);
                    }
                }
            }
        }
        for (Customer eventFault : customerMapper.selectIndustryCount(cond)) {
            Customer dto = new Customer();
            dto.setIndustry(eventFault.getIndustry());
            dto.setCount(eventFault.getCount());
            result.add(dto);
        }
        Map<String,Integer> collect = Maps.newHashMap();
        result.forEach(v->{
            if(!StringUtils.isEmpty(v.getIndustry())){
                collect.put(v.getIndustry(),v.getCount());
            }
        });

        Map<String,Integer> status = Maps.newHashMap();
        if(showType.equals("allStatus")){
            DictRest statusTypes = sysClient.get(MyConstant.DICT_INDUSTRY_TYPE);
            for (DictResult dictResult : statusTypes.getResult()) {
                Integer orDefault = collect.getOrDefault(dictResult.getLabel(), 0);
                status.put(dictResult.getLabel(),orDefault);
            }
        }else{
            collect.forEach((k1,v1) -> {
                status.put(k1,v1);
            });
        }
        return status;
    }
}
